#include "bsdf.h"
#include "scene.h"
#include "material.h"
namespace gemren
{
	void brdf_diffuse::announce_properties(const std::string& prefix, property_announcer announcer)
	{
		announcer((prefix+".color").c_str(), ANNOUNCED_STRING, val_to_str(display_color).c_str());
	}
	void brdf_specular::announce_properties(const std::string& prefix, property_announcer announcer)
	{
		announcer((prefix+".color").c_str(), ANNOUNCED_STRING, val_to_str(display_color).c_str());
	}
		
	void bsdf_fresnel::announce_properties(const std::string& prefix, property_announcer announcer)
	{
		announcer((prefix+".refraction").c_str(), ANNOUNCED_STRING, val_to_str(display_refr_ind).c_str());
	}
	err_t brdf_diffuse::set_my_string(std::string& name, const std::string& value)
	{
		if(name=="color")
		{
			display_color = spectrum_dynamic(value, 0.0f, 1.0f);
			color.copy_from_spectrum(display_color);
			return ERR_OK;
		}
		else return ERR_BAD_PROP;
	}
	err_t brdf_specular::set_my_string(std::string& name, const std::string& value)
	{
		if(name=="color")
		{
			display_color = spectrum_dynamic(value, 0.0f, 1.0f);
			color.copy_from_spectrum(display_color);
			return ERR_OK;
		}
		else return ERR_BAD_PROP;
	}

	err_t bsdf_fresnel::set_my_string(std::string& name, const std::string& value)
	{
		if(name=="refraction") 
		{
			display_refr_ind = spectrum_dynamic(value, 0, spectrum::infinity);
			refr_ind.copy_from_spectrum(display_refr_ind);
			return ERR_OK_RESET_CAUSTICS;
		}
		else return ERR_BAD_PROP;
	}

	void brdf_specular::bounce(random* ran, const scene* sc, intersection* i) const
	{
		i->outcoming = i->incoming - (2* embree::dot(i->normal, i->incoming))*i->normal;
		i->dim_light(color);
		i->reflectance = color.max();
	}
	float brdf_diffuse::pdf(const coord& incoming, const coord& outcoming, const coord& normal) const
	{
		return embree::clamp(embree::dot(normal, outcoming))/PI; 
	}
		
	void brdf_diffuse::bounce(random* ran, const scene* sc, intersection* i) const
	{
		i->outcoming = ran->cosined_hemisphere(i->normal);
		i->dim_light(color);
		i->reflectance = color.max();
	}

	color_s brdf_diffuse::throughput(const coord& incoming, const coord& outcoming, const coord& normal)const
	{
		if(embree::dot(normal, outcoming)<0) return color_s(0.0f);
		return color*(embree::clamp(embree::abs(embree::dot(incoming, normal)))/PI);
	}
	
	void bsdf_fresnel::bounce(random* ran, const scene* sc, intersection* i) const
	{
		
		float wl = i->get_wavelength(); //wavelength to compute fresnel with
		//embree::Col3f factor; //compensation for filtering colors
		if(wl<0)
		{
			wl = ran->getf();		
			embree::Col3f raycolor = sc->ray_color(wl);
			i->set_wavelength(wl);
		}
		float n = refr_ind[wl];
		
		coord norm(i->normal); 
		const coord &in(i->incoming);

		float cos_incoming = -embree::dot(i->normal, i->incoming);
		if(cos_incoming<0) //coming from inside
		{
			n = embree::rcp(n);
			norm = -norm;
			cos_incoming = -cos_incoming;
		}
		float rcpn = embree::rcp(n);

		coord surfacedir = i->incoming + cos_incoming*norm; //ray direction projected onto the surface plane
		surfacedir = rcpn*surfacedir;
		float sqrsine = embree::dot(surfacedir, surfacedir);
		
		float_type cos_refracted;
		
		float reflected = 1.01f;
		if(sqrsine<1) //nonzero chance of refr
		{//total reflection
			cos_refracted = sqrt( 1.0f-sqrsine); //cosine of refracted ray direction
			
			//compute fresnel coeff
			reflected =  0.5f*( //no polarisation
			embree::sqr((n*cos_refracted - cos_incoming)/ -(cos_incoming + n*cos_refracted))
			+
			embree::sqr((n*cos_incoming - cos_refracted)/ -(n*cos_incoming+ cos_refracted))
			);
			//...
			//if refract
			//refracted_dir = coord::normalize(surfacedir+refcos*nr); //is unit
			
		}
		if(ran->getf()<=reflected)
		{//go reflected
			i->outcoming = (i->incoming + (2* cos_incoming) * norm); //direction of reflected ray
			//i->importance *= 0.0;//reflected
			i->reflectance = 1;//reflected;
		}
		else 
		{//go refracted
			i->outcoming  = (surfacedir-cos_refracted*norm); //is unit
		/*	if(i->wavelength <0) //ray not yet monochromatic
			{
				i->wavelength = wl;
				i->dim_light(factor);
			}*/
			i->reflectance = 1;//1-reflected;
		}
	
	}

	void bsdf_fresnel::bounce_fast(random* ran, const scene* sc, intersection* i) const
	{
		float n = refr_ind[0.5f];
		
		coord norm(i->normal); 
		const coord &in(i->incoming);

		float cos_incoming = -embree::dot(i->normal, i->incoming);
		if(cos_incoming<0) //coming from inside
		{
			n = embree::rcp(n);
			norm = -norm;
			cos_incoming = -cos_incoming;
		}
		float rcpn = embree::rcp(n);

		coord surfacedir = i->incoming + cos_incoming*norm; //ray direction projected onto the surface plane
		surfacedir = rcpn*surfacedir;
		float sqrsine = embree::dot(surfacedir, surfacedir);
		
		float_type cos_refracted;
		
		float reflected = 1.01f;
		if(sqrsine<1) //nonzero chance of refr
		{//total reflection
			cos_refracted = sqrt( 1.0f-sqrsine); //cosine of refracted ray direction
			
			//compute fresnel coeff
			reflected =  0.5f*( //no polarisation
			embree::sqr((n*cos_refracted - cos_incoming)/ -(cos_incoming + n*cos_refracted))
			+
			embree::sqr((n*cos_incoming - cos_refracted)/ -(n*cos_incoming+ cos_refracted))
			);
			
			
		}
		if(ran->getf()<=reflected)
		{//go reflected
			i->outcoming = (i->incoming + (2* cos_incoming) * norm); //direction of reflected ray
			//i->importance *= 0.0;//reflected
			i->reflectance = reflected;
		}
		else 
		{//go refracted
			i->outcoming  = (surfacedir-cos_refracted*norm); //is unit
			i->reflectance = 1-reflected;
		}
	
	}

}